﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace teavch.Util.Formatter.SourceCode
{

	internal class HtmlFormatter : CodeFormatter
	{

		protected override StringBuilder Format( string input, bool quotedIdenfifiers, bool htmlBased )
		{
			StringBuilder sb = this.FormatNow( input, quotedIdenfifiers, htmlBased );
			return new StringBuilder( "<div class=\"divHtmlCode\" ><code>" + sb.ToString() + "</code></div>" );
		}

		private StringBuilder FormatNow( string input, bool quotedIdenfifiers, bool htmlBased )
		{
			StringBuilder result = new StringBuilder();

			WebBrowser wb = new WebBrowser();
			wb.Navigate( "about:Blank" );
			while ( wb.Document != null && wb.Document.Body != null ) { Application.DoEvents(); }

			wb.Document.OpenNew( true );
			wb.Document.Write( input );
			StringBuilder sReturn = new StringBuilder();

			HtmlElementCollection elements = wb.Document.GetElementsByTagName( "html" );
			string tempString = "";
			foreach ( HtmlElement element in elements )
			{
				if ( element != null && element.Children.Count > 0 )
				{
					tempString = this.CreateOpeningTag( element ).ToString();
					sReturn.Append( tempString );
					tempString = this.ParseElementWithChild( element ).ToString();
					sReturn.Append( tempString );
					tempString = this.CreateClosingTag( element ).ToString();
					sReturn.Append( tempString );
				}
				else
				{
					if ( element.TagName == "!" && element.OuterText == null )
					{
						sReturn = new StringBuilder( "<span class=\"CodeHtmlLtGt\">&lt;!</span><span class=\"CodeHtmlTagName\">DOCTYPE html</span><span class=\"CodeHtmlLtGt\">&gt;</span>" );
					}
					else
					{
						sReturn = this.ParseElementWithoutChild( element );
					}
				}
				result.Append( sReturn );
			}

			return result;
		}

		private StringBuilder ParseElementWithChild( HtmlElement element )
		{
			StringBuilder result = new StringBuilder();
			string tempString = "";

			foreach ( HtmlElement childe in element.Children )
			{
				if ( childe.Children != null && childe.Children.Count > 0 )
				{
					tempString = this.CreateOpeningTag( childe ).ToString();
					result.Append( tempString );
					tempString = this.ParseElementWithChild( childe ).ToString();
					result.Append( tempString );
					tempString = this.CreateClosingTag( childe ).ToString();
					result.Append( tempString );
				}
				else
				{
					tempString = this.ParseElementWithoutChild( childe ).ToString();
					result.Append( tempString );
				}
			}
			return result;
		}

		private MatchCollection GetAttributeList( string sTag )
		{
			StringBuilder result = new StringBuilder();
			string regularExpression = "(\\S+)=[\"']?((?:.(?![\"']?\\s+(?:\\S+)=|[>\"']))+.)[\"']?";
			Regex _regExHtml = new Regex( regularExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled );
			MatchCollection MatchList = _regExHtml.Matches( sTag );
			return MatchList;
		}

		private StringBuilder CreateOpeningTag( HtmlElement element )
		{
			StringBuilder result = new StringBuilder();
			MatchCollection MatchList = null;
			string sTag = "";
			if ( element.OuterHtml != null )
			{
				sTag = this.GetCurrentTagHead( element );
				MatchList = this.GetAttributeList( sTag );

				if ( MatchList != null && MatchList.Count > 0 )
				{
					result.Append( element.OuterHtml.Contains( "<" + element.TagName ) ? "<span class=\"CodeHtmlLtGt\">&lt;</span><span class=\"CodeHtmlTagName\">" + element.TagName.ToLower() + "</span>" : "" );
					foreach ( Match item in MatchList )
					{
						if ( item.Groups.Count == 3 )
						{
							result.Append( " <span class=\"CodeHtmlAttributeName\">" + item.Groups[ 1 ] + "</span><span class=\"CodeHtmlAttributeValue\">=\"" + item.Groups[ 2 ] + "\"</span>" );
						}
					}
					if ( element.Children.Count == 0 )
						result.Append( "<span class=\"CodeHtmlLtGt\">/&gt;</span>" );
					else
						result.Append( "<span class=\"CodeHtmlLtGt\">/&gt;</span><br />" );
				}
				else
				{
					if ( element.Children.Count == 0 )
						result.Append( element.OuterHtml.Contains( "<" + element.TagName + ">" ) ? "<span class=\"CodeHtmlLtGt\">&lt;</span><span class=\"CodeHtmlTagName\">" + element.TagName + "</span><span class=\"CodeHtmlLtGt\">&gt;</span>" : "" );
					else
						result.Append( element.OuterHtml.Contains( "<" + element.TagName + ">" ) ? "<span class=\"CodeHtmlLtGt\">&lt;</span><span class=\"CodeHtmlTagName\">" + element.TagName + "</span><span class=\"CodeHtmlLtGt\">&gt;</span><br />" : "" );
				}

			}
			return result;
		}

		private string GetCurrentTagHead( HtmlElement element )
		{
			string sTagHead = "";
			int _tagIndexStart = element.OuterHtml.IndexOf( "<" + element.TagName );
			int _tagIndexEnd = element.OuterHtml.IndexOf( ">", _tagIndexStart + 1 );
			sTagHead = element.OuterHtml.Substring( _tagIndexStart, ( _tagIndexEnd - _tagIndexStart ) + 1 );
			return sTagHead;
		}

		private StringBuilder CreateClosingTag( HtmlElement element )
		{
			StringBuilder result = new StringBuilder();
			result.Append( ( element.OuterHtml.Contains( "</" + element.TagName + ">" ) ? "<span class=\"CodeHtmlLtGt\">&lt;/</span><span class=\"CodeHtmlTagName\">" + element.TagName + "</span><span class=\"CodeHtmlLtGt\">&gt;</span><br />" : "<span class=\"CodeHtmlLtGt\"> /&gt;</span><br />" ) );
			return result;
		}

		private StringBuilder ReadTagContent( HtmlElement element )
		{
			StringBuilder result = new StringBuilder();
			result.Append( element.InnerText );
			return result;
		}

		private StringBuilder ParseElementWithoutChild( HtmlElement element )
		{
			StringBuilder result = new StringBuilder();
			string tempString = "";
			if ( element.OuterHtml != null )
			{
				tempString = this.CreateOpeningTag( element ).ToString();
				result.Append( tempString );

				if ( element.TagName.ToLower() == "script" )
				{
					tempString = this.GetScriptContent( element );
					result.Append( tempString );
				}
				else if ( element.TagName.ToLower() == "code" )
				{
					tempString = this.GetCodeContent( element );
					result.Append( tempString );
				}
				else
				{
					tempString = this.ReadTagContent( element ).ToString();
					result.Append( tempString );
				}
				tempString = this.CreateClosingTag( element ).ToString();
				result.Append( tempString );
			}

			return result;
		}

		protected override string[] GetTokens()
		{
			throw new NotImplementedException();
		}

		private string GetScriptContent( HtmlElement element )
		{
			string result = "";
			if ( element.InnerHtml != null && element.InnerHtml.Trim() != "" )
			{
				result = element.InnerHtml.ToString( SourceCodeLanguage.jQueryFormatter, true, true );
			}
			return result;
		}

		private string GetCodeContent( HtmlElement element )
		{
			string result = "";
			string completeTag = this.GetCurrentTagHead( element );
			MatchCollection attributeList = this.GetAttributeList( completeTag );
			string codeLanguage = "";
			foreach ( Match match in attributeList )
			{
				if ( match.Groups.Count == 3 )
				{
					if ( match.Groups[ 1 ].Value == "scl" )
					{
						codeLanguage = match.Groups[ 2 ].Value;
						break;
					}
				}
			}

			if ( element.InnerHtml != null && element.InnerHtml.Trim() != "" )
			{
				result = element.InnerHtml.ToString( codeLanguage, true, true );
			}
			return result;
		}

	}

}
